package xyz.labmem.main.config.aspect;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import xyz.labmem.main.application.controller.api.auth.request.LoginRequestBody;
import xyz.labmem.main.application.entity.sys.LoginLogs;
import xyz.labmem.main.result.ResultUtil;
import xyz.labmem.main.result.ResultVo;
import xyz.labmem.main.sys.StaticVariable;
import xyz.labmem.main.tool.NetUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author ：刘天予
 * @date ：Created in 2020/5/15 13:49
 * @description：Http请求切面（拦截器）
 * @modified By：
 * @version: $
 */
@Slf4j
@Aspect
@Component
public class HttpRequestAspect extends StaticVariable {

    private final MongoTemplate mongoTemplate;

    public HttpRequestAspect(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    /**
     * 以Controller为切入点
     */
    @Pointcut(value = "execution(public * xyz.labmem.main.application.controller..*.*(..))")
    public void HttpInfo() {
    }

    /**
     * 以adminLogin方法为切入点
     */
    @Pointcut(value = "execution(public * xyz.labmem.main.application.controller.api.auth.authApi.adminLogin(..))")
    public void adminLogin() {
    }

    /**
     * 简单的cc拦截
     */
    @Around("HttpInfo()")
    public Object InterceptCC(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.nonNull(sra)) {
            String ip = NetUtil.getIpAddr();
            HttpServletRequest request = sra.getRequest();

            if (IpBlacklist.get(ip, false) != null && !"/503".equals(request.getRequestURI())) {
                //获取切面目标的对象
                Signature signature = pjp.getSignature();
                MethodSignature methodSignature = (MethodSignature) signature;
                Method targetMethod = methodSignature.getMethod();
                //获取对象是否是APIController
                boolean isAPI = targetMethod.getDeclaringClass().isAnnotationPresent(RestController.class);
                if (isAPI)
                    return ResultUtil.free(503, "拒绝访问", null);
                else
                    return "redirect:/503";
            }
            if (IpCache.get(ip, false) == null) {
                IpCache.put(ip, ip);
                IpNum.put(ip, 1);
            } else {
                int ipnum = IpNum.get(ip);
                if (ipnum > 200)
                    IpBlacklist.put(ip, true);
                else
                    IpNum.put(ip, ipnum + 1);
            }
        }
        return pjp.proceed();
    }


    /**
     * ControllerHttp请求日志
     */
    @Around("HttpInfo()")
    public Object info(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        long init = 0L;
        String uri = null;
        if (Objects.nonNull(sra)) {
            init = System.currentTimeMillis();
            HttpServletRequest request = sra.getRequest();
            uri = request.getRequestURI();
            String method = request.getMethod();
            String params = Arrays.toString(pjp.getArgs());
            params = StringUtils.isEmpty(params) ? "" : " ; params: " + params;
            log.info("|>>>>>IP:" + NetUtil.getIpAddr() + ";请求URI: " + uri + " ; Method: " + method + params);
        }
        Object object = pjp.proceed();
        if (Objects.nonNull(sra)) {
            long over = System.currentTimeMillis() - init;
            log.info("|<<<<< '" + uri + "'  请求耗时：" + over + "ms >>>>>");
        }
        return object;
    }

    /**
     * admin登录日志
     */
    @Around("adminLogin()")
    public Object adminLoginLog(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        LoginLogs loginLogs = new LoginLogs();
        if (Objects.nonNull(sra)) {
            LoginRequestBody loginRequestBody = (LoginRequestBody)pjp.getArgs()[0];
            String userName = loginRequestBody.getUserName();
            loginLogs.setUserName(userName);
            loginLogs.setBrowser(NetUtil.getBrowserInfo());
            loginLogs.setId(IdUtil.simpleUUID());
            loginLogs.setIp(NetUtil.getIpAddr());
            loginLogs.setLoginDate(DateUtil.now());
        }
        Object object = pjp.proceed();
        if (Objects.nonNull(sra)) {
            ResultVo resultVo = (ResultVo) object;
            if (resultVo.getCode().equals(ResultUtil.SUCCESS_CODE)) {
                loginLogs.setMessage("登录成功");
            } else
                loginLogs.setMessage(resultVo.getMessage());
            mongoTemplate.insert(loginLogs);
            log.info("登录日志:" + loginLogs);
        }
        return object;
    }


}
